#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/interrupt.h>
#include <linux/delay.h>

// 定义 ft5x06 设备的 GPIO 描述符
// 用于保存 reset 和 interrupt 引脚的 GPIO 描述符
struct gpio_desc *reset_gpio, *irq_gpio;

// 保存 ft5x06 设备的 i2c 客户端对象指针
struct i2c_client *ft5x06_client;

// 读取 ft5x06 设备寄存器的函数
// 参数 reg_addr 是要读取的寄存器地址
// 返回值是读取到的寄存器值
int ft5x06_read_reg(u8 reg_addr)
{
    u8 data;
    // 定义两个 i2c_msg 结构体,分别表示写操作和读操作
    struct i2c_msg msgs[2] = {
        [0] = {
            .addr = ft5x06_client->addr, // 设备地址
            .flags = 0,        // 写操作
            .len = sizeof(reg_addr),
            .buf = &reg_addr,  // 写入要读取的寄存器地址
        },
        [1] = {
            .addr = ft5x06_client->addr,
            .flags = I2C_M_RD, // 读操作
            .len = sizeof(data),
            .buf = &data,      // 读取到的数据存储位置
        },
    };

    // 使用 i2c_transfer 函数进行 i2c 总线读取操作
    // 如果读取失败,返回 -EIO 错误码
    if (i2c_transfer(ft5x06_client->adapter, msgs, ARRAY_SIZE(msgs)) != ARRAY_SIZE(msgs)) {
        return -EIO;
    }

    return data; // 返回读取到的寄存器值
}

// 向 ft5x06 设备写入寄存器的函数
// 参数 reg_addr 是要写入的寄存器地址
// 参数 data 是要写入的数据
// 参数 len 是要写入的数据长度
void ft5x06_write_reg(u8 reg_addr, u8 *data, u16 len)
{
    u8 buff[256];
    struct i2c_msg msgs[] = {
        [0] = {
            .addr = ft5x06_client->addr, // 设备地址
            .flags = 0,        // 写操作
            .len = len + 1,    // 数据长度 + 寄存器地址长度
            .buf = buff,       // 数据缓冲区
        },
    };

    buff[0] = reg_addr;       // 写入寄存器地址
    memcpy(&buff[1], data, len); // 写入数据

    // 使用 i2c_transfer 函数进行 i2c 总线写入操作
    // 如果写入失败,直接返回
    if (i2c_transfer(ft5x06_client->adapter, msgs, ARRAY_SIZE(msgs)) != ARRAY_SIZE(msgs)) {
        return;
    }
}

// ft5x06 中断处理函数
// 参数 irq 是中断号
// 参数 args 是中断处理函数的参数
irqreturn_t ft5x06_handler(int irq, void *args)
{
    printk("This is ft5x06 handler\n");
    // 返回中断已处理标志
    return IRQ_RETVAL(IRQ_HANDLED);
}

// ft5x06 设备的探测函数
// 参数 client 是 i2c 客户端对象指针
// 参数 id 是 i2c 设备 ID
int ft5x06_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    int ret,value;
    printk("This is ft5x06 probe\n");
    ft5x06_client = client; // 保存 i2c 客户端对象指针

    // 获取 reset GPIO 描述符
    reset_gpio = gpiod_get_optional(&client->dev, "reset", 0);
    if (!reset_gpio) {
        printk("gpiod_get_optional reset gpio is error\n");
        return -1;
    }

    // 获取 irq GPIO 描述符
    irq_gpio = gpiod_get_optional(&client->dev, "interrupts", 0);
    if (!irq_gpio) {
        printk("gpiod_get_optional irq gpio is error\n");
        return -1;
    }

    // 设置 reset GPIO 为输出,并拉低 5ms 后拉高
    gpiod_direction_output(reset_gpio, 0);
    msleep(5);
    gpiod_direction_output(reset_gpio, 1);

    // 请求中断,设置为下降沿触发,单次触发
    ret = request_irq(client->irq, ft5x06_handler,
                     IRQ_TYPE_EDGE_FALLING | IRQF_ONESHOT, "ft5x06 irq", NULL);
    if (ret < 0) {
        printk("request irq is error\n");
        return -2;
    }

    // 写寄存器
    ft5x06_write_reg(0x80, &(u8){0x4b}, 1);

    // 读寄存器
    value = ft5x06_read_reg(0x80);
    printk("reg 0x80 is %x\n", value);

    return 0;
}

// ft5x06 设备的移除函数
// 参数 client 是 i2c 客户端对象指针
int ft5x06_remove(struct i2c_client *client)
{
    // 释放中断
    free_irq(client->irq, NULL);
    // 释放 GPIO 资源
    gpiod_put(reset_gpio);
    gpiod_put(irq_gpio);
    return 0;
}

// 定义 i2c_device_id 结构体数组,用于标识 ft5x06 设备
static const struct i2c_device_id ft5x06_id[] = {
    { "my-ft5x06", 0 },
    { }
};

// 定义 i2c_driver 结构体,描述 ft5x06 设备驱动
static struct i2c_driver ft5x06_driver = {
    .driver = {
        .name = "my-ft5x06",
        .owner = THIS_MODULE,
    },
    .probe = ft5x06_probe,
    .remove = ft5x06_remove,
    .id_table = ft5x06_id,
};

// 驱动初始化函数
static int __init ft5x06_driver_init(void)
{
    int ret;
    // 注册 I2C 设备驱动
    ret = i2c_add_driver(&ft5x06_driver);
    if (ret < 0) {
        printk("i2c_add_driver is error\n");
        return ret;
    }
    return 0;
}

// 驱动退出函数
static void __exit ft5x06_driver_exit(void)
{
    // 注销 I2C 设备驱动
    i2c_del_driver(&ft5x06_driver);
}

module_init(ft5x06_driver_init);
module_exit(ft5x06_driver_exit);
MODULE_LICENSE("GPL");
